package com.beiding.dbrequest.common;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.java.Log;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * 请求表
 * 主要做请求超时和响应回调
 * <p>
 * 幂等的保证 必须有响应 必须响应一次
 * 1.发送失败 发送时的异常情况或者发送时服务未启动,找不到可用目标服务等
 * 2.响应超时 等待较长时间做出超时处理
 * 3.正常响应
 */
@Log
public class RequestTable {

    private Map<Long, RequestLaunch> map = new ConcurrentHashMap<>();

    /**
     * 请求超时时间
     */
    @Getter @Setter
    private int timeout = 30000;

    public void put(RequestLaunch requestLaunch) {

        Long id = requestLaunch.getRequestMessage().getRequestId();
        map.put(id, requestLaunch);
        final Runnable timeoutCheck = () -> {
            synchronized (requestLaunch) {
                if (map.remove(id) == null) {
                    return;
                }
                ResponseCallback responseCallback = requestLaunch.getResponseCallback();
                if (responseCallback != null) {
                    try {
                        Message message = Message.responseFor(requestLaunch.getRequestMessage())
                                .status(Message.STATUS_TIMEOUT)
                                .msg("服务请求超时");
                        responseCallback.onResponse(message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    log.warning("无回调:" + id);
                }
            }
        };

        ScheduledFuture<?> task = SecondWorker.worker.schedule(timeoutCheck, timeout, TimeUnit.MILLISECONDS);
        requestLaunch.setTimeoutTask(task);
    }

    public void onResponse(Message message) {
        Long requestId = message.getRequestId();
        RequestLaunch launch = map.remove(requestId);
        if (launch != null) {
            //取消定时任务
            synchronized (launch) {
                cancelTimeout(launch);
                ResponseCallback responseCallback = launch.getResponseCallback();
                if (responseCallback != null) {
                    responseCallback.onResponse(message);
                }
            }
        } else {
            log.warning("找不到请求发起:" + requestId);
        }
    }

    private void cancelTimeout(RequestLaunch launch) {
        ScheduledFuture<?> timeoutTask = launch.getTimeoutTask();
        if (timeoutTask != null) {
            timeoutTask.cancel(false);
        }
    }


}
